
#pragma once

#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
#endif


//#include "libden/meta/is_stl_compatible.h"

#include "libden/meta/can_recursive_iteration.h"



namespace Den{

#if 0
    class MyIterator : public std::iterator<std::input_iterator_tag, int>
    {
        int* p;
    public:
        MyIterator(int* x) :p(x) {}
        MyIterator(const MyIterator& mit) : p(mit.p) {}
        MyIterator& operator++() {++p;return *this;}
        MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
        bool operator==(const MyIterator& rhs) {return p==rhs.p;}
        bool operator!=(const MyIterator& rhs) {return p!=rhs.p;}
        int& operator*() {return *p;}
    };
#endif

    #define dGEN_METHODS(_is_const_, _is_reverse_, _beg_, _end_)  \
    enum { IS_CONST   = _is_const_   };         \
    enum { IS_REVERSE = _is_reverse_ };         \
    template<class C> static                    \
        decltype(::std::declval<C>()._beg_())   \
            Beg(C& c) { return c._beg_(); }     \
                                                \
    template<class C> static                    \
        decltype(::std::declval<C>()._end_())   \
            End(C& c) { return c._end_(); }

    struct Way
    {
        struct begin   { dGEN_METHODS(0,0, begin,     end) };
        struct cbegin  { dGEN_METHODS(1,0, cbegin,   cend) };
        struct rbegin  { dGEN_METHODS(0,1, rbegin,   rend) };
        struct crbegin { dGEN_METHODS(1,1, crbegin, crend) };
    };
    #undef dGEN_METHODS

    namespace detail_iterator
    {
        template<class C, class M> struct Base
        {
            typedef decltype (  M::Beg( *static_cast<C*>(nullptr) )  )
                iterator;
        };



    }//namespace detail_iterator
   
    template<class Container, class ConcreteWay > 
    struct IteratorWrapper: detail_iterator::Base<Container, ConcreteWay>::iterator
    {
        typedef typename detail_iterator::Base<Container, ConcreteWay>::iterator
            Iter;
        typedef typename Iter::value_type
            value_type;

        typedef ConcreteWay
            MyWay;
        typedef typename ::std::add_const<Container>::type
            cv;
        typedef typename ::std::remove_const<Container>::type
            no_cv;

        enum { CAN_RECURSE = ::std::is_same<no_cv, value_type>::value  };

        enum { IS_WRAPPER = 1 };
        enum { IS_REVERSE = ConcreteWay::IS_REVERSE };
        enum { IS_CONST = ::std::is_const<Container>::value || 
            ConcreteWay::IS_CONST };

        typedef typename ::std::conditional<IS_CONST, cv, no_cv>::type
            MyContainer;

        friend 
            struct IteratorWrapper<cv, MyWay>;
        friend 
            struct IteratorWrapper<no_cv, MyWay>;

        IteratorWrapper()
            :Iter()
            ,mContainer(nullptr)
        {}
        IteratorWrapper(cv& c, const Iter& cur )
            :Iter(cur)
            ,mContainer(&c)
        {}
        IteratorWrapper(cv& c )
            :Iter( ConcreteWay::Beg(c) )
            ,mContainer(&c)
        {}
        IteratorWrapper(no_cv& c, const Iter& cur )
            :Iter(cur)
            ,mContainer(&c)
        {}
        IteratorWrapper(no_cv& c )
            :Iter( ConcreteWay::Beg(c) )
            ,mContainer(&c)
        {}

        IteratorWrapper(const IteratorWrapper& rhs)
            :Iter(rhs)
            ,mContainer(rhs.mContainer)
        {}

        template<class U>
        IteratorWrapper(const IteratorWrapper<U, ConcreteWay>& rhs)
            :Iter(rhs)
            ,mContainer(rhs.mContainer)
        {}

        IteratorWrapper& operator=(no_cv& c)
        {
            static_cast<Iter&>(*this) = MyWay::Beg(c);
            mContainer = &c;
            return *this;
        }

        IteratorWrapper& operator=(cv& c)
        {
            static_cast<Iter&>(*this) = MyWay::Beg(c);
            mContainer = &c;
            return *this;
        }

        template<class U>
        IteratorWrapper& operator=(const IteratorWrapper<U, ConcreteWay>& rhs)
        {
            static_cast<Iter&>(*this) = rhs;
            mContainer = rhs.mContainer;
            return *this;
        }

        operator bool()const  {  return !Empty(); }
        bool operator!()const {  return  Empty(); }

        bool Beg()const   
        {
            return mContainer && 
                static_cast<const Iter&>(*this)==MyWay::Beg(*mContainer);
        }
        bool End()const   
        {
            return  !mContainer || 
                static_cast<const Iter&>(*this)==MyWay::End(*mContainer);
        }
        bool Empty()const { return End(); }

        bool operator!=(const IteratorWrapper& rhs)const{ return !operator==(rhs); }

        bool operator==(const IteratorWrapper& rhs)const
        {
            const size_t empty_this  = Empty()?     1: 0;
            const size_t empty_other = rhs.Empty()? 1: 0;
            if(empty_this + empty_other == 2)
                return true;
            if(empty_this != empty_other)
                return false;

            const Iter& _rhs = static_cast<const Iter&>(rhs);
            const Iter& me = static_cast<const Iter&>(*this);
            return _rhs==me;
        }
    protected:
        MyContainer* mContainer;
    };

    template<class ConcreteWay,class Container> 
        IteratorWrapper<Container, ConcreteWay>
            MakeIterator(Container& c)
            {
                return IteratorWrapper<Container, ConcreteWay>(c);
            }

    template<class ConcreteWay,class Container, class Iter> 
        IteratorWrapper<Container, ConcreteWay>
            MakeIterator(Container& c, Iter&& i)
            {
                return IteratorWrapper<Container, ConcreteWay>(c, i);
            }

}//namespace Den


#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
    #pragma GCC diagnostic pop
#endif
